﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace DDDB
{

    [StructLayout(LayoutKind.Explicit, Pack = 1)]
    public struct DataHead
    {
        [FieldOffset(0)]
        public byte tag;//0 nothing //1 used //2 deleted
        public bool IsEmpty
        {
            get
            {
                return tag == 0;
            }
        }
        [FieldOffset(1)]
        UInt16 _block;//数据块长度
        [FieldOffset(3)]
        UInt32 _lenmix;
        [FieldOffset(6)]
        public DataPos nextpos;
        public uint len
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                return (_lenmix & 0xffffff) + 1;
            }
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            set
            {
                if (value == 0 || value >= 0x1000000)
                    throw new Exception("error len");
                _lenmix = (_lenmix & 0xff000000) | (value - 1);
            }
        }
        public uint useblock
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                return (uint)_block + 1;
            }
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            set
            {
                if (value == 0 || value >= 0x100)
                    throw new Exception("error len");
                _block = (UInt16)(value - 1);
            }
        }

    }

    //0簇记录特殊信息
    //数据文件 4K为一簇，0簇预留 最大65536个索引簇

    [StructLayout(LayoutKind.Explicit, Pack = 1)]
    public unsafe struct DataHeadWithKey
    {
        [FieldOffset(0)]
        public byte tag;//0 nothing //1 used //2 deleted
        public bool IsEmpty
        {
            get
            {
                return tag == 0;
            }
        }
        [FieldOffset(1)]
        UInt16 _block;//数据块长度
        [FieldOffset(3)]
        UInt32 _lenmix;
        [FieldOffset(6)]
        public DataPos nextpos;
        public uint len
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                return (_lenmix & 0xffffff) + 1;
            }
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            set
            {
                if (value == 0 || value >= 0x1000000)
                    throw new Exception("error len");
                _lenmix = (_lenmix & 0xff000000) | (value - 1);
            }
        }
        public uint useblock
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                return (uint)_block + 1;
            }
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            set
            {
                if (value == 0 || value >= 0x100)
                    throw new Exception("error len");
                _block = (UInt16)(value - 1);
            }
        }
        [FieldOffset(10)]
        //接下来有32个字节key
        public fixed byte key[32];
        //然后是data
        public string GetStringKey()
        {
            string str = "";
            for (var i = 0; i < 32; i++)
            {
                str += key[i].ToString("X02");
            }
            return str;
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void SetKey(byte[] keydata)
        {
            for (var i = 0; i < 32; i++)
            {
                key[i] = keydata[i];
            }
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool KeyEqual(byte[] keydata)
        {
            for (var i = 0; i < 32; i++)
            {
                if (key[i] != keydata[i])
                    return false;
            }
            return true;
        }

    }
    public class DataFile : IDisposable
    {
        System.IO.FileStream fs;
        public string filename
        {
            get;
            private set;
        }
        public UInt16 DataID
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get;
            private set;
        }
        public DataFile(string datapath, UInt16 dataid)
        {
            this.filename = System.IO.Path.Combine(datapath, "data_" + dataid.ToString("X04") + ".bin");
            this.DataID = dataid;
            if (System.IO.File.Exists(filename) == false)
            {
                fs = System.IO.File.Open(filename, FileMode.OpenOrCreate);
                SetWritePos(1);
                //增加16MB
                fs.Position = 16 * 1024 * 1024 - 1;
                fs.WriteByte(0);
            }
            else
            {
                fs = System.IO.File.Open(filename, FileMode.Open);
              
                byte[] buf = new byte[2];
                fs.Position = 0;
                fs.Read(buf, 0, 2);
                _writepos = BitConverter.ToUInt16(buf);

            }
        }

        /// <summary>
        /// Uncheck 接口不管数据是否合理，需要外部判断
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public DataHead Uncheck_ReadHead(int index)
        {
            if (index <= 0 || index >= 65535)
                throw new Exception("error data index");
            fs.Position = index * 4096;
            byte[] buf = new byte[10];
            fs.Read(buf, 0, 10);
            unsafe
            {
                fixed (byte* ptr = buf)
                {
                    return ((DataHead*)ptr)[0];
                }
            }
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public DataHeadWithKey Uncheck_ReadHeadWithKey(int index)
        {
            if (index <= 0 || index >= 65535)
                throw new Exception("error data index");
            fs.Position = index * 4096;
            byte[] buf = new byte[42];
            fs.Read(buf, 0, 42);
            unsafe
            {
                fixed (byte* ptr = buf)
                {
                    return ((DataHeadWithKey*)ptr)[0];
                }
            }
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public DataHeadWithKey Uncheck_ReadHeadWithKeyAndData(int index, out byte[] data)
        {
            if (index <= 0 || index >= 65535)
                throw new Exception("error data index");
            fs.Position = index * 4096;
            byte[] buf = new byte[42];
            fs.Read(buf, 0, 42);
            unsafe
            {
                fixed (byte* ptr = buf)
                {
                    DataHeadWithKey head = ((DataHeadWithKey*)ptr)[0];
                    data = new byte[head.len];
                    fs.Position = index * 4096 + 42;
                    fs.Read(data, 0, data.Length);
                    return head;
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public byte[] Uncheck_ReadData(int index, int datalen)
        {
            if (index <= 0 || index >= 65535)
                throw new Exception("error data index");
            if (datalen <= 0 || datalen > 256 * 255 * 255)
                throw new Exception("error datalen index");
            byte[] data = new byte[datalen];
            fs.Position = index * 4096 + 42;
            fs.Read(data, 0, datalen);
            return data;
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Uncheck_WriteHead(int index, DataHead head)
        {
            if (index <= 0 || index >= 65535)
                throw new Exception("error data index");

            byte[] buf = new byte[10];
            unsafe
            {
                fixed (byte* ptr = buf)
                {
                    ((DataHead*)ptr)[0] = head;
                }
            }
            fs.Position = index * 4096;
            fs.Write(buf, 0, 10);
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Uncheck_WriteHeadWithKey(int index, DataHeadWithKey headwithkey)
        {
            if (index <= 0 || index >= 65535)
                throw new Exception("error data index");

            byte[] buf = new byte[42];
            unsafe
            {
                fixed (byte* ptr = buf)
                {
                    ((DataHeadWithKey*)ptr)[0] = headwithkey;
                }
            }
            fs.Position = index * 4096;
            fs.Write(buf, 0, 42);
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Uncheck_WriteHeadWithKeyAndData(int index, DataHeadWithKey headwithkey, byte[] data)
        {
            if (index <= 0 || index >= 65535)
                throw new Exception("error data index");
            if (headwithkey.nextpos.datastart == index)
            {
                throw new Exception("what?");
            }
            byte[] buf = new byte[42];
            unsafe
            {
                fixed (byte* ptr = buf)
                {
                    ((DataHeadWithKey*)ptr)[0] = headwithkey;
                }
            }
            fs.Position = index * 4096 + 42;
            fs.Write(data);
            fs.Position = index * 4096;
            fs.Write(buf, 0, 42);
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Uncheck_WriteData(int index, byte[] data)
        {
            if (index <= 0 || index >= 65535)
                throw new Exception("error data index");

            fs.Position = index * 4096 + 42;
            fs.Write(data);
        }
        public enum FindKeyState
        {
            Error,
            NotMatch,
            LeaveThisFile,
            Match,
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public FindKeyState FindKey(byte[] key, ref DataPos pos,out DataHeadWithKey head, out DataPos lastpos,out DataHeadWithKey lasthead)
        {
            if (pos.datafile != this.DataID)
            {
                throw new Exception("not this file.");
            }
            if (pos.datastart == 0)
            {
                throw new Exception("error data.");
            }
            lastpos = new DataPos();
            lasthead= new DataHeadWithKey();
            while (true)
            {
                head = Uncheck_ReadHeadWithKey(pos.datastart);

                if (head.IsEmpty)
                {
                    return FindKeyState.Error;
                }
                else if (head.KeyEqual(key))
                {
                    pos = new DataPos { datafile = this.DataID, datastart = pos.datastart };
                    return FindKeyState.Match;
                }
                else if (head.nextpos.IsEmpty)
                {
                    return FindKeyState.NotMatch;
                }
                else if (head.nextpos.datafile != this.DataID)
                {
                    return FindKeyState.LeaveThisFile;
                }
                else
                {
                    lastpos = pos;
                    lasthead = head;
                    pos = new DataPos { datafile = this.DataID, datastart = head.nextpos.datastart };
                }

            }

        }
        UInt16 _writepos;
        public UInt16 WritePos
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                return _writepos;
            }
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void SetWritePos(UInt16 pos)
        {
            fs.Position = 0;
            _writepos = pos;
            fs.Write(BitConverter.GetBytes(_writepos));
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static uint CalcBlockSize(uint datalen)
        {
            uint totallen = 42 + datalen;

            uint needblock = totallen / 4096;
            if (totallen % 4096 > 0) needblock++;

            if (needblock < 8) needblock = 8;
            return needblock;
        }

        public void Dispose()
        {
            fs.Dispose();
        }

    }
}
